package jp.go.aist.six.vuln.repository;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mongodb.BasicDBObject;
import com.mongodb.CommandResult;
import com.mongodb.DB;
import com.mongodb.Mongo;
import com.mongodb.MongoURI;
import com.mysema.util.FileUtils;


@Ignore( "pending." )
public class AbstractMongoTest
{

    /**
     * Logger.
     */
    private static final Logger  _LOG_ = LoggerFactory.getLogger( AbstractMongoTest.class );


    public static final String  MONGOD_PATH = "T:\\mongodb-2.2.1\\bin\\mongod";
    public static final String  DB_PATH = "target/mongodb";
    public static final String  LOG_PATH = "mongodb-test.log";


    public static final String[]  MONGOD_COMMAND = new String[] {
        MONGOD_PATH,
        "--fork",
        "--smallfiles",
        "--dbpath", DB_PATH,
        "--logpath", LOG_PATH,
        "--logappend"
    };



    @Test
    public void shouldConnectToTestDatabase()
                    throws Exception
    {
        Mongo mongo = new Mongo();
        try {
            DB  test = mongo.getDB( "test" );
            assertThat( test.getCollectionNames(), is( not( null ) ) );
        } finally {
            mongo.close();
        }
    }



    @BeforeClass
    public static void startDatabaseAsAForkedProcess()
                    throws IOException, InterruptedException
    {
        _LOG_.debug( "begin setup..." );
        File  db_path = _ensureDbPathDoesNotExits();

        _LOG_.debug( "creating db path...: " + db_path );
        boolean  dir_created = db_path.mkdir();
        _LOG_.debug( "...db path created: " + db_path );
        assertThat( dir_created, is( true ) );

        List<String>  lines = _startMongoDbAsADaemon();
        assertThat( lines, not( nullValue() ) );

//        assertThat( lines.get(0).startsWith( "forked process: " ), is( true ) );
//        assertThat( lines.get(1).startsWith( "all output going to: " ), is( true ) );
//        assertThat( lines.size(), is( 2 ) );
        _assertThatConnectionToMongodbIsPossible();

        _LOG_.debug( "...setup completed." );
    }



    /**
     *
     * @throws InterruptedException
     * @throws UnknownHostException
     */
    private static void _assertThatConnectionToMongodbIsPossible()
                    throws InterruptedException, UnknownHostException
    {
        _LOG_.debug( "connecting to db..." );

        Mongo  server = null;
        try {
            while (server == null) {
                Thread.sleep( 250 );
                server = new MongoURI( "mongodb://127.0.0.1" ).connect();
            }
//            assertThat( server.getDatabaseNames().size(), is( 1 ) );
        } finally {
            server.close();
        }

        _LOG_.debug( "...connected to db." );
    }



    /**
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    protected static List<String> _startMongoDbAsADaemon()
                    throws IOException, InterruptedException
    {
        _LOG_.debug( "starting mongodb server..." );
        ProcessBuilder  builder = new ProcessBuilder( MONGOD_COMMAND );
        builder.directory( new File( "target" ) );
        builder.redirectErrorStream( true );
        Process  process = builder.start();
        _LOG_.debug( "...mongodb server started." );

        _LOG_.debug( "waiting for mongodb server..." );
        BufferedReader output_reader = new BufferedReader( new InputStreamReader( process.getInputStream() ) );
        List<String>  lines = new ArrayList<String>();
        String  output;
        while ((output = output_reader.readLine()) != null)
        {
            lines.add( output );
        }
        process.waitFor();
        _LOG_.debug( "...mongodb server terminated." );

        int  exit_value = process.exitValue();
        _LOG_.debug( "mongodb server exit value: " + exit_value );
        assertThat( exit_value, is( 0 ) );

        return lines;
    }



    /**
     *
     * @return
     * @throws IOException
     */
    protected static File _ensureDbPathDoesNotExits()
                    throws IOException
    {
        File  db_path = new File( DB_PATH );
        if (db_path.exists()) {
            _LOG_.debug( "db path exists: " + db_path );
            _LOG_.debug( "deleting path...: " + db_path );
            FileUtils.delete( db_path );
            _LOG_.debug( "...path deleted: " + db_path );
            assertThat( db_path.exists(), is( false ) );
        } else {
            _LOG_.debug( "db path NOT exist: " + db_path );
        }

        return db_path;
    }


    @AfterClass
    public static void shutdownDatabase()
                    throws IOException
    {
        Mongo  mongo = new Mongo();
        try {
            DB  db = mongo.getDB( "admin" );
            CommandResult  shutdownResult = db.command( new BasicDBObject( "shutdown", 1 ) );
            shutdownResult.throwOnError();
//            fail( "Expecting to loose mongodb connection on shutdown." );
        } catch (Throwable e) {
//            _LOG_.error( "shutdown", e );
            _LOG_.debug( e.getMessage() );
            assertThat( e.getMessage(), is( "can't call something : /127.0.0.1:27017/admin") );
        } finally {
            try {
                mongo.close();
            } catch (Exception ex) {
                //ignorable
            }
//            _ensureDbPathDoesNotExits();
        }
    }

}
